package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sc.nft.dao.*;
import com.sc.nft.dto.MetaMapSettleDTO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.SaasTaskBurialPointDTO;
import com.sc.nft.entity.dto.WantBuyOrderCreateDTO;
import com.sc.nft.entity.dto.WantBuyOrderSellDTO;
import com.sc.nft.entity.vo.*;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.pool.UserPool;
import com.sc.nft.service.*;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.util.OrderNoUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 刘国航
 * @desc
 * @since 2024/12/19 20:05
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WantBuyOrderServiceImpl implements WantBuyOrderService {

    private final UserInfoDao userInfoDao;
    private final WantBuyOrderDao wantBuyOrderDao;
    private final WantBuyOrderConsumePropDao wantBuyOrderConsumePropDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final EquityPropsDao equityPropsDao;
    private final SecondCollectionConfigDao secondCollectionConfigDao;
    private final SecondEquityPropsConfigDao secondEquityPropsConfigDao;
    private final SecondOrderDao secondOrderDao;
    private final SecondEquityPropsOrderService secondEquityPropsOrderService;
    private final UserCollectionDao userCollectionDao;
    private final UserEquityPropsDao userEquityPropsDao;
    private final UserWalletService userWalletService;
    private final PayOrderDao payOrderDao;
    private final ExitConsensusUserDao exitConsensusUserDao;
    private final UserCollectionConsensusDao userCollectionConsensusDao;
    private final UserWalletDao userWalletDao;
    private final UserWalletDetailDao userWalletDetailDao;
    private final ExpenditureRecordDao expenditureRecordDao;
    private final UserConsensusService userConsensusService;
    private final MessageQueueHelper messageQueueHelper;
    private final FilterIncomeListDao filterIncomeListDao;
    private final SecondCollectionDao secondCollectionDao;
    private final UserCollectionService userCollectionService;
    private final PayOrderItemDao payOrderItemDao;
    private final SecondEquityPropsDao secondEquityPropsDao;
    private final SecondEquityPropsOrderDao secondEquityPropsOrderDao;
    private final UserEquityPropsService userEquityPropsService;
    private final StaticDataService staticDataService;

    @Override
    public IPage<WantBuyOrderVO> page(Page page, String userTel, SecondWantBuyOrderStatusEnum status) {
        Long userId = null;
        if (StrUtil.isNotBlank(userTel)) {
            UserInfo userInfo = userInfoDao.getByUserTel(userTel);
            if (ObjectUtil.isNull(userInfo)) {
                return page;
            }
        }
        Page<WantBuyOrder> wantBuyOrderPage = wantBuyOrderDao.page(page, userId, status);
        //region 账号
        List<Long> userIdList = CollectionUtil.isNotEmpty(wantBuyOrderPage.getRecords()) ? wantBuyOrderPage.getRecords().stream().map(WantBuyOrder::getUserId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<UserInfo> userInfoList = CollectionUtil.isNotEmpty(userIdList) ? userInfoDao.getByIdList(userIdList) : Lists.newArrayList();
        Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 藏品
        List<Long> collectionIdList = CollectionUtil.isNotEmpty(wantBuyOrderPage.getRecords()) ? wantBuyOrderPage.getRecords()
                .stream()
                .filter(v -> Objects.equals(v.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION))
                .map(WantBuyOrder::getRelationId)
                .distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<DigitalCollection> digitalCollectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
        Map<Long, DigitalCollection> digitalCollectionMap = CollectionUtil.isNotEmpty(digitalCollectionList) ? digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 道具
        List<Long> propIdList = CollectionUtil.isNotEmpty(wantBuyOrderPage.getRecords()) ? wantBuyOrderPage.getRecords()
                .stream()
                .filter(v -> Objects.equals(v.getOrderType(), SecondWantBuyOrderTypeEnum.EQUITY_PROPS))
                .map(WantBuyOrder::getRelationId)
                .distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<EquityProps> equityPropsList = CollectionUtil.isNotEmpty(propIdList) ? equityPropsDao.listByIds(propIdList) : Lists.newArrayList();
        Map<Long, EquityProps> equityPropsMap = CollectionUtil.isNotEmpty(equityPropsList) ? equityPropsList.stream().collect(Collectors.toMap(EquityProps::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        return wantBuyOrderPage.convert(v -> {
            WantBuyOrderVO wantBuyOrderVO = new WantBuyOrderVO();
            wantBuyOrderVO.setId(v.getId());
            wantBuyOrderVO.setUserId(v.getUserId());
            UserInfo userInfo = userInfoMap.get(v.getUserId());
            wantBuyOrderVO.setUserTel(ObjectUtil.isNotNull(userInfo) ? userInfo.getUserTel() : "");
            wantBuyOrderVO.setNickName(ObjectUtil.isNotNull(userInfo) ? userInfo.getNickName() : "");
            String relationName = "";
            switch (v.getOrderType()) {
                case COLLECTION:
                    DigitalCollection digitalCollection = digitalCollectionMap.get(v.getRelationId());
                    if (Objects.nonNull(digitalCollection)) {
                        relationName = digitalCollection.getFirstTitle();
                    }
                    break;
                case EQUITY_PROPS:
                    EquityProps equityProps = equityPropsMap.get(v.getRelationId());
                    if (Objects.nonNull(equityProps)) {
                        relationName = equityProps.getFirstTitle();
                    }
                    break;
            }
            wantBuyOrderVO.setRelationName(relationName);
            wantBuyOrderVO.setWantBuyPrice(v.getWantBuyPrice());
            wantBuyOrderVO.setWantBuyQuantity(v.getWantBuyQuantity());
            wantBuyOrderVO.setWantBuyAmount(v.getWantBuyPrice().multiply(new BigDecimal(v.getWantBuyQuantity())));
            wantBuyOrderVO.setUsableWantBuyAmount(v.getWantBuyPrice().multiply(new BigDecimal(v.getUsableWantBuyQuantity())));
            wantBuyOrderVO.setCompleteCount(v.getWantBuyQuantity() - v.getUsableWantBuyQuantity());
            wantBuyOrderVO.setStatus(v.getStatus());
            wantBuyOrderVO.setStatusName(v.getStatus().getDescription());
            wantBuyOrderVO.setStartTime(v.getStartTime());
            wantBuyOrderVO.setEndTime(v.getEndTime());
            return wantBuyOrderVO;
        });
    }

    @Override
    public IPage<WantBuyOrderAdminRecordVO> recordPage(Page page, Long id, String sellUserTel, Date startCreateTime, Date endCreateTime, Date startPayTime, Date endPayTime) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        if (Objects.isNull(wantBuyOrder)) {
            return page;
        }
        Long salesUserId = null;
        if (StrUtil.isNotBlank(sellUserTel)) {
            UserInfo userInfo = userInfoDao.getByUserTel(sellUserTel);
            if (Objects.isNull(userInfo)) {
                return page;
            }
            salesUserId = userInfo.getId();
        }
        if (Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION)) {

            IPage<SecondOrder> secondOrderIPage = secondOrderDao.wantBuyOrderPage(page, wantBuyOrder.getId(), salesUserId, startCreateTime, endCreateTime, startPayTime, endPayTime);

            List<Long> collectionIdList = CollectionUtil.isNotEmpty(secondOrderIPage.getRecords()) ? secondOrderIPage.getRecords().stream().map(SecondOrder::getCollectionId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
            List<DigitalCollection> digitalCollectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
            Map<Long, DigitalCollection> digitalCollectionMap = CollectionUtil.isNotEmpty(digitalCollectionList) ? digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();

            List<Long> salesUserIdList = CollectionUtil.isNotEmpty(secondOrderIPage.getRecords()) ? secondOrderIPage.getRecords().stream().map(SecondOrder::getSalesUserId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
            List<UserInfo> userInfoList = CollectionUtil.isNotEmpty(salesUserIdList) ? userInfoDao.getByIdList(salesUserIdList) : Lists.newArrayList();
            Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : Maps.newHashMap();

            return secondOrderIPage.convert(v -> {
                WantBuyOrderAdminRecordVO wantBuyOrderAdminRecordVO = new WantBuyOrderAdminRecordVO();
                DigitalCollection digitalCollection = digitalCollectionMap.get(v.getCollectionId());
                wantBuyOrderAdminRecordVO.setRelationName(ObjectUtil.isNotNull(digitalCollection) ? digitalCollection.getFirstTitle() : "");
                wantBuyOrderAdminRecordVO.setOrderNo(v.getOrderNo());
                wantBuyOrderAdminRecordVO.setBuyPrice(v.getBuyPrice());
                wantBuyOrderAdminRecordVO.setCharge(v.getCharge());
                wantBuyOrderAdminRecordVO.setGas(v.getGas());
                UserInfo userInfo = userInfoMap.get(v.getSalesUserId());
                wantBuyOrderAdminRecordVO.setSellUserTel(ObjectUtil.isNotNull(userInfo) ? userInfo.getUserTel() : "");
                wantBuyOrderAdminRecordVO.setCreateTime(v.getCreateTime());
                wantBuyOrderAdminRecordVO.setPayTime(v.getPayTime());
                return wantBuyOrderAdminRecordVO;
            });
        }

        IPage<SecondEquityPropsOrder> secondEquityPropsOrderIPage = secondEquityPropsOrderService.wantBuyOrderPage(page, wantBuyOrder.getId(), salesUserId, startCreateTime, endCreateTime, startPayTime, endPayTime);
        List<Long> equityPropsIdList = CollectionUtil.isNotEmpty(secondEquityPropsOrderIPage.getRecords()) ? secondEquityPropsOrderIPage.getRecords().stream().map(SecondEquityPropsOrder::getEquityPropsId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<EquityProps> equityPropsList = CollectionUtil.isNotEmpty(equityPropsIdList) ? equityPropsDao.listByIds(equityPropsIdList) : Lists.newArrayList();
        Map<Long, EquityProps> equityPropsMap = CollectionUtil.isNotEmpty(equityPropsList) ? equityPropsList.stream().collect(Collectors.toMap(EquityProps::getId, Function.identity())) : Maps.newHashMap();

        List<Long> salesUserIdList = CollectionUtil.isNotEmpty(secondEquityPropsOrderIPage.getRecords()) ? secondEquityPropsOrderIPage.getRecords().stream().map(SecondEquityPropsOrder::getSalesUserId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<UserInfo> userInfoList = CollectionUtil.isNotEmpty(salesUserIdList) ? userInfoDao.getByIdList(salesUserIdList) : Lists.newArrayList();
        Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : Maps.newHashMap();

        return secondEquityPropsOrderIPage.convert(v -> {
            WantBuyOrderAdminRecordVO wantBuyOrderAdminRecordVO = new WantBuyOrderAdminRecordVO();
            EquityProps equityProps = equityPropsMap.get(v.getEquityPropsId());
            wantBuyOrderAdminRecordVO.setRelationName(ObjectUtil.isNotNull(equityProps) ? equityProps.getFirstTitle() : "");
            wantBuyOrderAdminRecordVO.setOrderNo(v.getOrderNo());
            wantBuyOrderAdminRecordVO.setBuyPrice(v.getBuyPrice());
            wantBuyOrderAdminRecordVO.setCharge(v.getCharge());
            UserInfo userInfo = userInfoMap.get(v.getSalesUserId());
            wantBuyOrderAdminRecordVO.setSellUserTel(ObjectUtil.isNotNull(userInfo) ? userInfo.getUserTel() : "");
            wantBuyOrderAdminRecordVO.setCreateTime(v.getCreateTime());
            wantBuyOrderAdminRecordVO.setPayTime(v.getPayTime());
            return wantBuyOrderAdminRecordVO;
        });
    }

    @Override
    public WantBuyOrderEnableVO enableWantBuy(SecondWantBuyOrderTypeEnum orderType, Long relationId) {
        Boolean enable = Boolean.FALSE;
        switch (orderType) {
            case COLLECTION:
                DigitalCollection digitalCollection = digitalCollectionDao.getById(relationId);
                if (Objects.nonNull(digitalCollection)) {
                    SecondCollectionConfig secondCollectionConfig = secondCollectionConfigDao.getByCollectionIdAndStatus(relationId);
                    if (Objects.nonNull(secondCollectionConfig)) {
                        enable = secondCollectionConfig.getEnableWantBuy();
                    }
                }
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(relationId);
                if (Objects.nonNull(equityProps)) {
                    SecondEquityPropsConfig secondEquityPropsConfig = secondEquityPropsConfigDao.getLastOneByEquityPropsId(relationId);
                    if (Objects.nonNull(secondEquityPropsConfig)) {
                        enable = secondEquityPropsConfig.getEnableWantBuy();
                    }
                }
                break;
        }
        WantBuyOrderEnableVO wantBuyOrderEnableVO = new WantBuyOrderEnableVO();
        wantBuyOrderEnableVO.setEnable(enable);
        return wantBuyOrderEnableVO;
    }

    @Override
    public WantBuyOrderOpenCreateVO openCreate(UserInfo userInfo, SecondWantBuyOrderTypeEnum orderType, Long relationId) {
        WantBuyOrderOpenCreateVO wantBuyOrderOpenCreateVO = new WantBuyOrderOpenCreateVO();
        wantBuyOrderOpenCreateVO.setOrderType(orderType);
        wantBuyOrderOpenCreateVO.setRelationId(relationId);
        switch (orderType) {
            case COLLECTION:
                DigitalCollection digitalCollection = digitalCollectionDao.getById(relationId);
                Assert.notNull(digitalCollection, () -> new GlobalRunTimeException("藏品不存在"));
                SecondCollectionConfig secondCollectionConfig = secondCollectionConfigDao.getByCollectionIdAndStatus(relationId);
                Assert.notNull(secondCollectionConfig, () -> new GlobalRunTimeException("藏品未开启集售"));
                Assert.isTrue(secondCollectionConfig.getEnableWantBuy(), () -> new GlobalRunTimeException("藏品未开启求购"));
                wantBuyOrderOpenCreateVO.setRelationName(digitalCollection.getFirstTitle());
                wantBuyOrderOpenCreateVO.setCreationContent(digitalCollection.getCreationContent());
                wantBuyOrderOpenCreateVO.setLastBuyPrice(secondOrderDao.getLastPrice(relationId));
                wantBuyOrderOpenCreateVO.setMinWantBuyPrice(secondCollectionConfig.getMinWantBuyPrice());
                wantBuyOrderOpenCreateVO.setMaxWantBuyPrice(secondCollectionConfig.getMaxWantBuyPrice());
                wantBuyOrderOpenCreateVO.setWantBuyQuantity(secondCollectionConfig.getWantBuyQuantity());
                wantBuyOrderOpenCreateVO.setEnableWantBuyHoldCondition(secondCollectionConfig.getEnableWantBuyHoldCondition());
                wantBuyOrderOpenCreateVO.setEnableWantBuyConsumeProp(secondCollectionConfig.getEnableWantBuyConsumeProp());
                if (secondCollectionConfig.getEnableWantBuyHoldCondition()) {
                    wantBuyOrderOpenCreateVO.setWantBuyHoldCollectionId(secondCollectionConfig.getWantBuyHoldCollectionId());
                    DigitalCollection wantBuyHoldCollection = digitalCollectionDao.getById(secondCollectionConfig.getWantBuyHoldCollectionId());
                    wantBuyOrderOpenCreateVO.setWantBuyHoldCollectionName(Objects.nonNull(wantBuyHoldCollection) ? wantBuyHoldCollection.getFirstTitle() : null);
                    wantBuyOrderOpenCreateVO.setHoldCollection(userCollectionDao.getByUserIdAndCollectionIdCount(userInfo.getId(), relationId) > 0);
                }
                if (secondCollectionConfig.getEnableWantBuyConsumeProp()) {
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropId(secondCollectionConfig.getWantBuyConsumePropId());
                    EquityProps wantBuyConsumeProp = equityPropsDao.getById(secondCollectionConfig.getWantBuyConsumePropId());
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropName(Objects.nonNull(wantBuyConsumeProp) ? wantBuyConsumeProp.getFirstTitle() : null);
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropQuantity(secondCollectionConfig.getWantBuyConsumePropQuantity());
                    wantBuyOrderOpenCreateVO.setHoldPropCount(userEquityPropsDao.getHavingCountByUserId(relationId, userInfo.getId()));
                }
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(relationId);
                Assert.notNull(equityProps, () -> new GlobalRunTimeException("道具不存在"));
                SecondEquityPropsConfig secondEquityPropsConfig = secondEquityPropsConfigDao.getLastOneByEquityPropsId(relationId);
                Assert.notNull(secondEquityPropsConfig, () -> new GlobalRunTimeException("道具未开启集售"));
                Assert.isTrue(secondEquityPropsConfig.getEnableWantBuy(), () -> new GlobalRunTimeException("道具未开启求购"));
                wantBuyOrderOpenCreateVO.setRelationName(equityProps.getFirstTitle());
                wantBuyOrderOpenCreateVO.setCreationContent(equityProps.getCreationContent());
                wantBuyOrderOpenCreateVO.setLastBuyPrice(secondEquityPropsOrderService.getLastPrice(relationId));
                wantBuyOrderOpenCreateVO.setMinWantBuyPrice(secondEquityPropsConfig.getMinWantBuyPrice());
                wantBuyOrderOpenCreateVO.setMaxWantBuyPrice(secondEquityPropsConfig.getMaxWantBuyPrice());
                wantBuyOrderOpenCreateVO.setWantBuyQuantity(secondEquityPropsConfig.getWantBuyQuantity());
                wantBuyOrderOpenCreateVO.setEnableWantBuyHoldCondition(secondEquityPropsConfig.getEnableWantBuyHoldCondition());
                wantBuyOrderOpenCreateVO.setEnableWantBuyConsumeProp(secondEquityPropsConfig.getEnableWantBuyConsumeProp());
                if (secondEquityPropsConfig.getEnableWantBuyHoldCondition()) {
                    wantBuyOrderOpenCreateVO.setWantBuyHoldCollectionId(secondEquityPropsConfig.getWantBuyHoldCollectionId());
                    DigitalCollection wantBuyHoldCollection = digitalCollectionDao.getById(secondEquityPropsConfig.getWantBuyHoldCollectionId());
                    wantBuyOrderOpenCreateVO.setWantBuyHoldCollectionName(Objects.nonNull(wantBuyHoldCollection) ? wantBuyHoldCollection.getFirstTitle() : null);
                    wantBuyOrderOpenCreateVO.setHoldCollection(userCollectionDao.getByUserIdAndCollectionIdCount(userInfo.getId(), relationId) > 0);
                }
                if (secondEquityPropsConfig.getEnableWantBuyConsumeProp()) {
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropId(secondEquityPropsConfig.getWantBuyConsumePropId());
                    EquityProps wantBuyConsumeProp = equityPropsDao.getById(secondEquityPropsConfig.getWantBuyConsumePropId());
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropName(Objects.nonNull(wantBuyConsumeProp) ? equityProps.getFirstTitle() : null);
                    wantBuyOrderOpenCreateVO.setWantBuyConsumePropQuantity(secondEquityPropsConfig.getWantBuyConsumePropQuantity());
                    wantBuyOrderOpenCreateVO.setHoldPropCount(userEquityPropsDao.getHavingCountByUserId(relationId, userInfo.getId()));
                }
                break;
        }
        StaticData staticData = staticDataService.getByType("want_buy_order_notice");
        wantBuyOrderOpenCreateVO.setWantBuyNotice(Objects.nonNull(staticData) && StrUtil.isNotBlank(staticData.getValue()) ? staticData.getValue() : "");
        return wantBuyOrderOpenCreateVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(UserInfo userInfo, WantBuyOrderCreateDTO dto, String ip) {
        Assert.isTrue(StrUtil.equals(dto.getPaymentPassword(), UserPool.getUser().getTransactionPassword()), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_PASSWORD_WAS_ENTERED_INCORRECTLY));
        Assert.isTrue(dto.getWantBuyQuantity() > 0, () -> new GlobalRunTimeException("求购数量必须大于0"));
        Boolean enableWantBuyHoldCondition = null;
        Boolean enableWantBuyConsumeProp = null;
        Long wantBuyHoldCollectionId = null;
        Long wantBuyConsumePropId = null;
        Integer wantBuyConsumePropQuantity = null;
        BigDecimal charge = null;
        BigDecimal minCharge = null;
        BigDecimal gas = null;
        switch (dto.getOrderType()) {
            case COLLECTION:
                //region 藏品求购单
                DigitalCollection digitalCollection = digitalCollectionDao.getById(dto.getRelationId());
                Assert.notNull(digitalCollection, () -> new GlobalRunTimeException("藏品不存在"));
                SecondCollectionConfig secondCollectionConfig = secondCollectionConfigDao.getByCollectionIdAndStatus(dto.getRelationId());
                Assert.notNull(secondCollectionConfig, () -> new GlobalRunTimeException("藏品未开启寄售"));
                Assert.isTrue(secondCollectionConfig.getEnableWantBuy(), () -> new GlobalRunTimeException("藏品未开启求购"));
                Assert.isTrue(dto.getWantBuyPrice().compareTo(secondCollectionConfig.getMinWantBuyPrice()) >= 0, () -> new GlobalRunTimeException("求购单价不能低于最低求购价"));
                Assert.isTrue(dto.getWantBuyPrice().compareTo(secondCollectionConfig.getMaxWantBuyPrice()) <= 0, () -> new GlobalRunTimeException("求购单价不能高于最高求购价"));
                Assert.isTrue(dto.getWantBuyQuantity() <= secondCollectionConfig.getWantBuyQuantity(), () -> new GlobalRunTimeException("求购数量不能大于单次最高求购数量"));
                enableWantBuyHoldCondition = secondCollectionConfig.getEnableWantBuyHoldCondition();
                enableWantBuyConsumeProp = secondCollectionConfig.getEnableWantBuyConsumeProp();
                wantBuyHoldCollectionId = secondCollectionConfig.getWantBuyHoldCollectionId();
                wantBuyConsumePropId = secondCollectionConfig.getWantBuyConsumePropId();
                wantBuyConsumePropQuantity = secondCollectionConfig.getWantBuyConsumePropQuantity();
                charge = secondCollectionConfig.getCharge();
                gas = secondCollectionConfig.getGas();
                //endregion
                break;
            case EQUITY_PROPS:
                //region 道具求购单
                EquityProps equityProps = equityPropsDao.getById(dto.getRelationId());
                Assert.notNull(equityProps, () -> new GlobalRunTimeException("道具不存在"));
                SecondEquityPropsConfig secondEquityPropsConfig = secondEquityPropsConfigDao.getLastOneByEquityPropsId(dto.getRelationId());
                Assert.notNull(secondEquityPropsConfig, () -> new GlobalRunTimeException("道具未开启集售"));
                Assert.isTrue(secondEquityPropsConfig.getEnableWantBuy(), () -> new GlobalRunTimeException("道具未开启求购"));
                Assert.isTrue(dto.getWantBuyPrice().compareTo(secondEquityPropsConfig.getMinWantBuyPrice()) >= 0, () -> new GlobalRunTimeException("求购单价不能低于最低求购价"));
                Assert.isTrue(dto.getWantBuyPrice().compareTo(secondEquityPropsConfig.getMaxWantBuyPrice()) <= 0, () -> new GlobalRunTimeException("求购单价不能高于最高求购价"));
                Assert.isTrue(dto.getWantBuyQuantity() <= secondEquityPropsConfig.getWantBuyQuantity(), () -> new GlobalRunTimeException("求购数量不能大于单次最高求购数量"));
                enableWantBuyHoldCondition = secondEquityPropsConfig.getEnableWantBuyHoldCondition();
                enableWantBuyConsumeProp = secondEquityPropsConfig.getEnableWantBuyConsumeProp();
                wantBuyHoldCollectionId = secondEquityPropsConfig.getWantBuyHoldCollectionId();
                wantBuyConsumePropId = secondEquityPropsConfig.getWantBuyConsumePropId();
                wantBuyConsumePropQuantity = secondEquityPropsConfig.getWantBuyConsumePropQuantity();
                charge = secondEquityPropsConfig.getCharge();
                minCharge = secondEquityPropsConfig.getMinCharge();
                //endregion
                break;
        }

        //region 持有藏品
        if (enableWantBuyHoldCondition) {
            Assert.isTrue(userCollectionDao.getByUserIdAndCollectionIdCount(userInfo.getId(), wantBuyHoldCollectionId) > 0, () -> new GlobalRunTimeException("条件不满足"));
        }
        //endregion
        //region 持有道具
        List<UserEquityProps> userEquityPropsList = null;
        if (enableWantBuyConsumeProp) {
            int size = dto.getWantBuyQuantity() * wantBuyConsumePropQuantity;
            userEquityPropsList = userEquityPropsDao.getHavingListByUserIdAndEquityPropsIdAndLimit(wantBuyConsumePropId, userInfo.getId(), size);
            Assert.isTrue(CollectionUtil.isNotEmpty(userEquityPropsList) && userEquityPropsList.size() == size, () -> new GlobalRunTimeException("条件不满足"));
        }
        //endregion
        //region 查询余额、原石
        UserWallet userWallet = userWalletService.getByUserId(userInfo.getId());
        //提现余额
        BigDecimal balance = userWallet.getBalance();
        //支付余额
        BigDecimal payBalance = userWallet.getPayBalance();
        BigDecimal sumBalance = balance.add(payBalance);
        BigDecimal payAmount = dto.getWantBuyPrice().multiply(new BigDecimal(dto.getWantBuyQuantity()));
        Assert.isFalse(payAmount.compareTo(sumBalance) == 1, () -> new GlobalRunTimeException(ErrorCode.BALANCE_NO_ENOUGH));
        //endregion
        //region 扣减余额、原石
        WalletDetailTypeEnum detailTypeEnum = null;
        switch (dto.getOrderType()) {
            case COLLECTION:
                detailTypeEnum = WalletDetailTypeEnum.WANT_BUY_COLLECTION_PAY;
                break;
            case EQUITY_PROPS:
                detailTypeEnum = WalletDetailTypeEnum.WANT_BUY_PROPS_PAY;
                break;
        }
        BigDecimal payBalanceAmount = BigDecimal.ZERO;
        BigDecimal balanceAmount = BigDecimal.ZERO;
        if (payBalance.compareTo(payAmount) >= 0) { //如果支付账户足够 直接从支付账户扣
            payBalanceAmount = payAmount;
            Assert.isTrue(userWalletService.otherSubBalance(userWallet, payAmount, null, detailTypeEnum, UserBalanceTypeEnum.PAY_BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
        } else {//如果支付账户没钱
            if (payBalance.compareTo(BigDecimal.ZERO) == 0) {//支付账户没钱,直接扣提现账户
                balanceAmount = payAmount;
                Assert.isTrue(userWalletService.otherSubBalance(userWallet, payAmount, null, detailTypeEnum, UserBalanceTypeEnum.BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
            } else {//如果支付账户有钱 但是不够
                //除支付账户
                Assert.isTrue(userWalletService.otherSubBalance(userWallet, payBalance, null, detailTypeEnum, UserBalanceTypeEnum.PAY_BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
                payBalanceAmount = payBalance;
                BigDecimal subtract = payAmount.subtract(payBalance);
                Assert.isTrue(userWalletService.otherSubBalance(userWallet, subtract, null, detailTypeEnum, UserBalanceTypeEnum.BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
                balanceAmount = sumBalance;
            }
        }
        //endregion
        DateTime now = DateUtil.date();
        //region 求购单
        WantBuyOrder wantBuyOrder = new WantBuyOrder();
        wantBuyOrder.setUserId(userInfo.getId());
        wantBuyOrder.setOrderType(dto.getOrderType());
        wantBuyOrder.setRelationId(dto.getRelationId());
        wantBuyOrder.setWantBuyPrice(dto.getWantBuyPrice());
        wantBuyOrder.setWantBuyQuantity(dto.getWantBuyQuantity());
        wantBuyOrder.setEnableWantBuyHoldCondition(enableWantBuyHoldCondition);
        wantBuyOrder.setEnableWantBuyConsumeProp(enableWantBuyConsumeProp);
        wantBuyOrder.setUsableWantBuyQuantity(dto.getWantBuyQuantity());
        wantBuyOrder.setWantBuyHoldCollectionId(wantBuyHoldCollectionId);
        wantBuyOrder.setWantBuyConsumePropId(wantBuyConsumePropId);
        wantBuyOrder.setWantBuyConsumePropQuantity(wantBuyConsumePropQuantity);
        wantBuyOrder.setCharge(charge);
        wantBuyOrder.setMinCharge(minCharge);
        wantBuyOrder.setGas(gas);
        wantBuyOrder.setStatus(SecondWantBuyOrderStatusEnum.RELEASE);
        wantBuyOrder.setStartTime(now);
        wantBuyOrder.setEndTime(DateUtil.offsetHour(now, 24));
        wantBuyOrder.setPayAmount(payAmount);
        wantBuyOrder.setPayBalanceAmount(payBalanceAmount);
        wantBuyOrder.setUsablePayBalanceAmount(payBalanceAmount);
        wantBuyOrder.setBalanceAmount(balanceAmount);
        wantBuyOrder.setUsableBalanceAmount(balanceAmount);
        wantBuyOrder.setRefundAmount(BigDecimal.ZERO);
        wantBuyOrder.setRefundProp(0);
        wantBuyOrder.setVersion(1);
        Assert.isTrue(wantBuyOrderDao.save(wantBuyOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        //endregion 求购单
        //region 求购单占用的道具
        if (CollectionUtil.isNotEmpty(userEquityPropsList)) {
            int sort = 1;
            for (UserEquityProps userEquityProps : userEquityPropsList) {
                WantBuyOrderConsumeProp wantBuyOrderConsumeProp = new WantBuyOrderConsumeProp();
                wantBuyOrderConsumeProp.setWantBuyOrderId(wantBuyOrder.getId());
                wantBuyOrderConsumeProp.setUserEquityPropsId(userEquityProps.getId());
                wantBuyOrderConsumeProp.setSort(sort++);
                wantBuyOrderConsumeProp.setStatus(SecondWantBuyOrderConsumePropStatusEnum.OCCUPY);
                Assert.isTrue(wantBuyOrderConsumePropDao.save(wantBuyOrderConsumeProp), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
                Assert.isTrue(userEquityPropsDao.updateFromStatusToStatus(userEquityProps.getId(), UserEquityPropsHoldingStatusEnum.NORMAL, UserEquityPropsHoldingStatusEnum.WANT_BUY_LOCK, UserEquityConsignStatusEnum.NOT_CONSIGN), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
            }
        }
        //endregion
        //region 支付订单
        PayOrder payOrder = new PayOrder();
        payOrder.setUserId(userInfo.getId());
        payOrder.setPayType(PayMethodEnum.WALLET_PAY);
        payOrder.setReqTime(DateUtil.date());
        payOrder.setCloseTime(DateUtil.date());
        payOrder.setOrderNo(OrderNoUtil.getOrderNo());
        payOrder.setReqIp(ip);
        payOrder.setOrderType(PayTypeEnum.WANT_BUY);
        payOrder.setPayAmount(payAmount);
        payOrder.setBalanceAmount(balanceAmount);
        payOrder.setPayBalanceAmount(payBalanceAmount);
        payOrder.setChannel(PayChannelEnum.NATIVE);
        payOrder.setStatus(PayStatusEnum.SUCCESS);
        payOrder.setPayTime(DateUtil.date());
        Assert.isTrue(payOrderDao.save(payOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        //endregion
    }

    @Override
    public IPage<WantBuyOrderWebPageVO> page(UserInfo userInfo, Page page, SecondWantBuyOrderTypeEnum orderType, Long relationId) {
        if (Objects.equals(orderType, SecondWantBuyOrderTypeEnum.COLLECTION)) {
            DigitalCollection digitalCollection = digitalCollectionDao.getById(relationId);
            if (Objects.isNull(digitalCollection)) {
                return page;
            }
            if (Objects.equals(digitalCollection.getCollectionType(), DigitalMarketTypeEnum.ECOLOGY)) {
                //生态藏品
                ExitConsensusUser exitConsensusUser = exitConsensusUserDao.getByUserId(userInfo.getId());
                if (ObjectUtil.isNotNull(exitConsensusUser) && exitConsensusUser.getStatus()) {
                    //region 非共识区
                    return wantBuyOrderDao.exitConsensusPage(page, orderType, relationId, userInfo.getId()).convert(v -> {
                        v.setCreationContent(digitalCollection.getCreationContent());
                        return v;
                    });
                    //endregion
                } else {
                    //region 共识区
                    return wantBuyOrderDao.consensusPage(page, orderType, relationId, userInfo.getId()).convert(v -> {
                        v.setCreationContent(digitalCollection.getCreationContent());
                        return v;
                    });
                    //endregion
                }
            }
        }
        IPage<WantBuyOrder> wantBuyOrderIPage = wantBuyOrderDao.page(page, orderType, relationId, userInfo.getId());
        //region 藏品
        List<Long> collectionIdList = CollectionUtil.isNotEmpty(wantBuyOrderIPage.getRecords()) ? wantBuyOrderIPage.getRecords().stream().filter(wantBuyOrder -> Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION)).map(WantBuyOrder::getRelationId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<DigitalCollection> digitalCollectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
        Map<Long, DigitalCollection> digitalCollectionMap = CollectionUtil.isNotEmpty(digitalCollectionList) ? digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 道具
        List<Long> propIdList = CollectionUtil.isNotEmpty(wantBuyOrderIPage.getRecords()) ? wantBuyOrderIPage.getRecords().stream().filter(wantBuyOrder -> Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.EQUITY_PROPS)).map(WantBuyOrder::getRelationId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<EquityProps> equityPropsList = CollectionUtil.isNotEmpty(propIdList) ? equityPropsDao.listByIds(propIdList) : Lists.newArrayList();
        Map<Long, EquityProps> equityPropsMap = CollectionUtil.isNotEmpty(equityPropsList) ? equityPropsList.stream().collect(Collectors.toMap(EquityProps::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        return wantBuyOrderIPage.convert(v -> {
            WantBuyOrderWebPageVO wantBuyOrderWebPageVO = new WantBuyOrderWebPageVO();
            wantBuyOrderWebPageVO.setId(v.getId());
            wantBuyOrderWebPageVO.setWantBuyPrice(v.getWantBuyPrice());
            wantBuyOrderWebPageVO.setUsableWantBuyQuantity(v.getUsableWantBuyQuantity());
            String creationContent = "";
            switch (v.getOrderType()) {
                case COLLECTION:
                    DigitalCollection digitalCollection = digitalCollectionMap.get(v.getRelationId());
                    creationContent = Objects.nonNull(digitalCollection) ? digitalCollection.getCreationContent() : "";
                    break;
                case EQUITY_PROPS:
                    EquityProps equityProps = equityPropsMap.get(v.getRelationId());
                    creationContent = Objects.nonNull(equityProps) ? equityProps.getCreationContent() : "";
                    break;
            }
            wantBuyOrderWebPageVO.setCreationContent(creationContent);
            return wantBuyOrderWebPageVO;
        });
    }

    @Override
    public WantBuyOrderWebVO queryUsableWantBuyQuantity(UserInfo userInfo, Long id) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        Assert.notNull(wantBuyOrder, () -> new GlobalRunTimeException("求购单不存在"));
        Assert.isTrue(Objects.equals(wantBuyOrder.getStatus(), SecondWantBuyOrderStatusEnum.RELEASE), () -> new GlobalRunTimeException("求购单已结束"));
        Assert.isTrue(wantBuyOrder.getEndTime().after(DateUtil.date()), () -> new GlobalRunTimeException("求购单已结束"));
        //region 藏品名称/道具名称
        String relationName = "";
        //是否生态藏品
        Boolean isEcology = Boolean.TRUE;
        //是否在共识区 仅生态藏品
        Boolean isConsensus = null;
        //共识度 仅生态藏品且在共识区
        BigDecimal consensusNum = null;
        //剩余求购数量
        Integer usableWantBuyQuantity = wantBuyOrder.getUsableWantBuyQuantity();
        //是否持有
        Boolean isHold = Boolean.FALSE;
        switch (wantBuyOrder.getOrderType()) {
            case COLLECTION:
                DigitalCollection digitalCollection = digitalCollectionDao.getById(wantBuyOrder.getRelationId());
                if (Objects.nonNull(digitalCollection)) {
                    relationName = digitalCollection.getFirstTitle();
                    isEcology = Objects.equals(digitalCollection.getCollectionType(), DigitalMarketTypeEnum.ECOLOGY);
                    if (isEcology) {
                        ExitConsensusUser sellExitConsensusUser = exitConsensusUserDao.getByUserId(userInfo.getId());
                        isConsensus = ObjectUtil.isNull(sellExitConsensusUser) || !sellExitConsensusUser.getStatus();
                        if (isConsensus) {
                            //针对生态藏品，出售人在共识区，查询共识度
                            UserCollectionConsensus userCollectionConsensus = userCollectionConsensusDao.getBuyUserIdAndCollectionId(userInfo.getId(), wantBuyOrder.getRelationId());
                            consensusNum = ObjectUtil.isNull(userCollectionConsensus) ? BigDecimal.ZERO : userCollectionConsensus.getConsensusNum();
                        }
                        ExitConsensusUser wantBuyExitConsensusUser = exitConsensusUserDao.getByUserId(wantBuyOrder.getUserId());
                        Boolean wantBuyIsConsensus = ObjectUtil.isNull(wantBuyExitConsensusUser) || !wantBuyExitConsensusUser.getStatus();
                        if (!Objects.equals(isConsensus, wantBuyIsConsensus)) {
                            //针对生态藏品，求购人和出售人如果没有同时在共识区，或没有同时在非共识区，则剩余求购数量设置为0，不能出售
                            usableWantBuyQuantity = 0;
                        }
                    }
                    isHold = userCollectionDao.whetherTheUserOwnsACollection(userInfo.getId(), wantBuyOrder.getRelationId());
                }
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(wantBuyOrder.getRelationId());
                if (Objects.nonNull(equityProps)) {
                    relationName = equityProps.getFirstTitle();
                    isHold = userEquityPropsDao.getHavingCountByUserId(wantBuyOrder.getRelationId(), userInfo.getId()) > 0;
                }
                break;
        }
        //endregion
        WantBuyOrderWebVO wantBuyOrderWebVO = new WantBuyOrderWebVO();
        wantBuyOrderWebVO.setOrderType(wantBuyOrder.getOrderType());
        wantBuyOrderWebVO.setRelationId(wantBuyOrder.getRelationId());
        wantBuyOrderWebVO.setRelationName(relationName);
        wantBuyOrderWebVO.setWantBuyPrice(wantBuyOrder.getWantBuyPrice());
        wantBuyOrderWebVO.setUsableWantBuyQuantity(usableWantBuyQuantity);
        wantBuyOrderWebVO.setCharge(wantBuyOrder.getCharge());
        wantBuyOrderWebVO.setMinCharge(wantBuyOrder.getMinCharge());
        wantBuyOrderWebVO.setGas(wantBuyOrder.getGas());
        wantBuyOrderWebVO.setIsEcology(isEcology);
        wantBuyOrderWebVO.setIsConsensus(isConsensus);
        wantBuyOrderWebVO.setConsensusNum(consensusNum);
        wantBuyOrderWebVO.setIsHold(isHold);
        return wantBuyOrderWebVO;
    }

    @Override
    public IPage<WantBuyOrderQueryHoldVO> queryHoldPage(UserInfo userInfo, Page page, Long id) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        if (Objects.isNull(wantBuyOrder)) {
            return page;
        }
        if (Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION)) {
            DigitalCollection digitalCollection = digitalCollectionDao.getById(wantBuyOrder.getRelationId());
            return userCollectionDao.userCollectionPage(page, userInfo.getId(), wantBuyOrder.getRelationId()).convert(v -> {
                WantBuyOrderQueryHoldVO wantBuyOrderQueryHoldVO = new WantBuyOrderQueryHoldVO();
                wantBuyOrderQueryHoldVO.setOrderType(wantBuyOrder.getOrderType());
                wantBuyOrderQueryHoldVO.setUserRelationId(v.getId());
                wantBuyOrderQueryHoldVO.setBuyPrice(v.getBuyPrice());
                wantBuyOrderQueryHoldVO.setNumbering(String.format("%d/%d", v.getNumbering(), digitalCollection.getSumCount()));
                return wantBuyOrderQueryHoldVO;
            });
        }
        DateTime now = DateUtil.date();
        return userEquityPropsDao.havingByUserIdPage(page, wantBuyOrder.getRelationId(), userInfo.getId()).convert(v -> {
            WantBuyOrderQueryHoldVO wantBuyOrderQueryHoldVO = new WantBuyOrderQueryHoldVO();
            wantBuyOrderQueryHoldVO.setOrderType(wantBuyOrder.getOrderType());
            wantBuyOrderQueryHoldVO.setUserRelationId(v.getId());
            wantBuyOrderQueryHoldVO.setBuyPrice(v.getBuyPrice());
            wantBuyOrderQueryHoldVO.setIsLongTerm(((v.getExpiredTime().getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) >= 365);
            wantBuyOrderQueryHoldVO.setExpiredTime(v.getExpiredTime());
            return wantBuyOrderQueryHoldVO;
        });
    }

    @Override
    public IPage<WantBuyOrderMyPageVO> myWantBuyPage(UserInfo userInfo, Page page, SecondWantBuyOrderTypeEnum orderType, Long relationId) {
        Date now = DateUtil.date();
        IPage<WantBuyOrder> wantBuyOrderIPage = wantBuyOrderDao.page(page, userInfo.getId(), orderType, relationId);
        //region 藏品
        List<Long> collectionIdList = CollectionUtil.isNotEmpty(wantBuyOrderIPage.getRecords()) ? wantBuyOrderIPage.getRecords().stream().filter(wantBuyOrder -> Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION)).map(WantBuyOrder::getRelationId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<DigitalCollection> digitalCollectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
        Map<Long, DigitalCollection> digitalCollectionMap = CollectionUtil.isNotEmpty(digitalCollectionList) ? digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 道具
        List<Long> propIdList = CollectionUtil.isNotEmpty(wantBuyOrderIPage.getRecords()) ? wantBuyOrderIPage.getRecords().stream().filter(wantBuyOrder -> Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.EQUITY_PROPS)).map(WantBuyOrder::getRelationId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<EquityProps> equityPropsList = CollectionUtil.isNotEmpty(propIdList) ? equityPropsDao.listByIds(propIdList) : Lists.newArrayList();
        Map<Long, EquityProps> equityPropsMap = CollectionUtil.isNotEmpty(equityPropsList) ? equityPropsList.stream().collect(Collectors.toMap(EquityProps::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        return wantBuyOrderIPage.convert(v -> {
            WantBuyOrderMyPageVO wantBuyOrderMyPageVO = new WantBuyOrderMyPageVO();
            wantBuyOrderMyPageVO.setId(v.getId());
            wantBuyOrderMyPageVO.setOrderType(v.getOrderType());
            wantBuyOrderMyPageVO.setRelationId(v.getRelationId());
            String relationName = "";
            String creationContent = "";
            switch (v.getOrderType()) {
                case COLLECTION:
                    DigitalCollection digitalCollection = digitalCollectionMap.get(v.getRelationId());
                    relationName = Objects.nonNull(digitalCollection) ? digitalCollection.getFirstTitle() : "";
                    creationContent = Objects.nonNull(digitalCollection) ? digitalCollection.getCreationContent() : "";
                    break;
                case EQUITY_PROPS:
                    EquityProps equityProps = equityPropsMap.get(v.getRelationId());
                    relationName = Objects.nonNull(equityProps) ? equityProps.getFirstTitle() : "";
                    creationContent = Objects.nonNull(equityProps) ? equityProps.getCreationContent() : "";
                    break;
            }
            wantBuyOrderMyPageVO.setRelationName(relationName);
            wantBuyOrderMyPageVO.setCreationContent(creationContent);
            wantBuyOrderMyPageVO.setWantBuyQuantity(v.getWantBuyQuantity());
            wantBuyOrderMyPageVO.setWantBuyPrice(v.getWantBuyPrice());
            wantBuyOrderMyPageVO.setCompleteCount(v.getWantBuyQuantity() - v.getUsableWantBuyQuantity());
            wantBuyOrderMyPageVO.setStatus(v.getStatus());
            wantBuyOrderMyPageVO.setStatusName(v.getStatus().getDescription());
            wantBuyOrderMyPageVO.setStartTime(v.getStartTime());
            wantBuyOrderMyPageVO.setRemainingTime(Objects.equals(v.getStatus(), SecondWantBuyOrderStatusEnum.RELEASE) ? timeDifference(v.getEndTime(), now) : null);
            return wantBuyOrderMyPageVO;
        });
    }

    @Override
    public WantBuyOrderMyDetailVO myWantBuyDetail(Long id) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        Assert.notNull(wantBuyOrder, () -> new GlobalRunTimeException("求购单不存在"));
        String relationName = "";
        String creationContent = "";
        switch (wantBuyOrder.getOrderType()) {
            case COLLECTION:
                DigitalCollection digitalCollection = digitalCollectionDao.getById(wantBuyOrder.getRelationId());
                relationName = Objects.nonNull(digitalCollection) ? digitalCollection.getFirstTitle() : "";
                creationContent = Objects.nonNull(digitalCollection) ? digitalCollection.getCreationContent() : "";
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(wantBuyOrder.getRelationId());
                relationName = Objects.nonNull(equityProps) ? equityProps.getFirstTitle() : "";
                creationContent = Objects.nonNull(equityProps) ? equityProps.getCreationContent() : "";
                break;
        }
        WantBuyOrderMyDetailVO wantBuyOrderMyDetailVO = new WantBuyOrderMyDetailVO();
        wantBuyOrderMyDetailVO.setId(wantBuyOrder.getId());
        wantBuyOrderMyDetailVO.setOrderType(wantBuyOrder.getOrderType());
        wantBuyOrderMyDetailVO.setRelationId(wantBuyOrder.getRelationId());
        wantBuyOrderMyDetailVO.setRelationName(relationName);
        wantBuyOrderMyDetailVO.setCreationContent(creationContent);
        wantBuyOrderMyDetailVO.setWantBuyQuantity(wantBuyOrder.getWantBuyQuantity());
        wantBuyOrderMyDetailVO.setWantBuyPrice(wantBuyOrder.getWantBuyPrice());
        wantBuyOrderMyDetailVO.setWantBuyAmount(wantBuyOrder.getWantBuyPrice().multiply(new BigDecimal(wantBuyOrder.getWantBuyQuantity())));
        wantBuyOrderMyDetailVO.setCompleteCount(wantBuyOrder.getWantBuyQuantity() - wantBuyOrder.getUsableWantBuyQuantity());
        wantBuyOrderMyDetailVO.setCompleteAmount(wantBuyOrder.getWantBuyPrice().multiply(new BigDecimal(wantBuyOrder.getWantBuyQuantity() - wantBuyOrder.getUsableWantBuyQuantity())));
        wantBuyOrderMyDetailVO.setStatus(wantBuyOrder.getStatus());
        wantBuyOrderMyDetailVO.setStatusName(wantBuyOrder.getStatus().getDescription());
        wantBuyOrderMyDetailVO.setStartTime(wantBuyOrder.getStartTime());
        if (Objects.equals(wantBuyOrder.getStatus(), SecondWantBuyOrderStatusEnum.RELEASE)) {
            wantBuyOrderMyDetailVO.setRemainingTime(timeDifference(wantBuyOrder.getEndTime(), DateUtil.date()));
        } else {
            wantBuyOrderMyDetailVO.setEndTime(wantBuyOrder.getEndTime());
            wantBuyOrderMyDetailVO.setRefundAmount(wantBuyOrder.getRefundAmount());
            wantBuyOrderMyDetailVO.setRefundProp(wantBuyOrder.getRefundProp());
        }
        return wantBuyOrderMyDetailVO;
    }

    @Override
    public IPage<WantBuyOrderRecordVO> myRecordPage(Page page, Long id) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        if (Objects.isNull(wantBuyOrder)) {
            return page;
        }
        if (Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION)) {
            IPage<SecondOrder> secondOrderIPage = secondOrderDao.wantBuyOrderPage(page, wantBuyOrder.getId());
            List<Long> collectionIdList = CollectionUtil.isNotEmpty(secondOrderIPage.getRecords()) ? secondOrderIPage.getRecords().stream().map(SecondOrder::getCollectionId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
            List<DigitalCollection> digitalCollectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
            Map<Long, DigitalCollection> digitalCollectionMap = CollectionUtil.isNotEmpty(digitalCollectionList) ? digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
            return secondOrderIPage.convert(v -> {
                WantBuyOrderRecordVO wantBuyOrderRecordVO = new WantBuyOrderRecordVO();
                DigitalCollection digitalCollection = digitalCollectionMap.get(v.getCollectionId());
                wantBuyOrderRecordVO.setRelationName(Objects.nonNull(digitalCollection) ? digitalCollection.getFirstTitle() : "");
                wantBuyOrderRecordVO.setCreationContent(Objects.nonNull(digitalCollection) ? digitalCollection.getCreationContent() : "");
                wantBuyOrderRecordVO.setBuyPrice(v.getBuyPrice());
                wantBuyOrderRecordVO.setCreateTime(v.getCreateTime());
                wantBuyOrderRecordVO.setOrderNo(v.getOrderNo());
                return wantBuyOrderRecordVO;
            });
        }
        IPage<SecondEquityPropsOrder> secondEquityPropsOrderIPage = secondEquityPropsOrderService.wantBuyOrderPage(page, wantBuyOrder.getId());
        List<Long> propIdList = CollectionUtil.isNotEmpty(secondEquityPropsOrderIPage.getRecords()) ? secondEquityPropsOrderIPage.getRecords().stream().map(SecondEquityPropsOrder::getEquityPropsId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<EquityProps> equityPropsList = CollectionUtil.isNotEmpty(propIdList) ? equityPropsDao.listByIds(propIdList) : Lists.newArrayList();
        Map<Long, EquityProps> equityPropsMap = CollectionUtil.isNotEmpty(equityPropsList) ? equityPropsList.stream().collect(Collectors.toMap(EquityProps::getId, Function.identity())) : Maps.newHashMap();
        return secondEquityPropsOrderIPage.convert(v -> {
            WantBuyOrderRecordVO wantBuyOrderRecordVO = new WantBuyOrderRecordVO();
            EquityProps equityProps = equityPropsMap.get(v.getEquityPropsId());
            wantBuyOrderRecordVO.setRelationName(Objects.nonNull(equityProps) ? equityProps.getFirstTitle() : "");
            wantBuyOrderRecordVO.setCreationContent(Objects.nonNull(equityProps) ? equityProps.getCreationContent() : "");
            wantBuyOrderRecordVO.setBuyPrice(v.getBuyPrice());
            wantBuyOrderRecordVO.setCreateTime(v.getCreateTime());
            wantBuyOrderRecordVO.setOrderNo(v.getOrderNo());
            return wantBuyOrderRecordVO;
        });
    }

    @Override
    public List<WantBuyOrder> queryReleasesFiveMinuteLater() {
        LambdaQueryWrapper<WantBuyOrder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(WantBuyOrder::getStatus, SecondWantBuyOrderStatusEnum.RELEASE);
        queryWrapper.le(WantBuyOrder::getEndTime, DateUtil.date().offsetNew(DateField.MINUTE, 5));
        return wantBuyOrderDao.list(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void close(Long id) {
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        Assert.notNull(wantBuyOrder, () -> new GlobalRunTimeException("求购单不存在"));
        Assert.isTrue(Objects.equals(wantBuyOrder.getStatus(), SecondWantBuyOrderStatusEnum.RELEASE), () -> new GlobalRunTimeException("求购单已关闭"));
        //region 归还道具
        List<WantBuyOrderConsumeProp> wantBuyOrderConsumePropList = wantBuyOrderConsumePropDao.queryByOrderIdAndStatus(wantBuyOrder.getId(), SecondWantBuyOrderConsumePropStatusEnum.OCCUPY);
        if (CollectionUtil.isNotEmpty(wantBuyOrderConsumePropList)) {
            wantBuyOrder.setRefundProp(wantBuyOrder.getRefundProp() + wantBuyOrderConsumePropList.size());
            wantBuyOrderConsumePropList.forEach(wantBuyOrderConsumeProp -> {
                UserEquityProps userEquityProps = userEquityPropsDao.getById(wantBuyOrderConsumeProp.getUserEquityPropsId());
                Assert.notNull(userEquityProps, () -> new GlobalRunTimeException("道具不存在"));
                wantBuyOrderConsumeProp.setStatus(SecondWantBuyOrderConsumePropStatusEnum.RETURN);
                Assert.isTrue(wantBuyOrderConsumePropDao.updateById(wantBuyOrderConsumeProp), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
                Assert.isTrue(userEquityPropsDao.updateFromStatusToStatus(userEquityProps.getId(), UserEquityPropsHoldingStatusEnum.WANT_BUY_LOCK, UserEquityPropsHoldingStatusEnum.NORMAL, UserEquityConsignStatusEnum.NOT_CONSIGN), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
            });
        }
        //endregion
        //region 归还金额
        UserWallet userWallet = userWalletDao.getByUserId(wantBuyOrder.getUserId());
        BigDecimal refundAmount = BigDecimal.ZERO;
        if (wantBuyOrder.getUsableBalanceAmount().compareTo(BigDecimal.ZERO) > 0) {
            refundAmount = refundAmount.add(wantBuyOrder.getUsableBalanceAmount());

            userWalletDao.addBalance(wantBuyOrder.getUserId(), wantBuyOrder.getUsableBalanceAmount());

            UserWalletDetail userWalletDetail = new UserWalletDetail();
            userWalletDetail.setUserId(wantBuyOrder.getUserId());
            userWalletDetail.setType(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? WalletDetailTypeEnum.WANT_BUY_COLLECTION_REFUND : WalletDetailTypeEnum.WANT_BUY_PROPS_REFUND);
            userWalletDetail.setBalanceType(UserBalanceTypeEnum.BALANCE);
            userWalletDetail.setAmount(wantBuyOrder.getUsableBalanceAmount());
            userWalletDetail.setCharge(BigDecimal.ZERO);
            userWalletDetail.setRealAmount(wantBuyOrder.getUsableBalanceAmount());
            userWalletDetail.setAfterBalance(userWallet.getPayBalance().add(wantBuyOrder.getUsableBalanceAmount()));
            userWalletDetail.setBeforeBalance(userWallet.getPayBalance());
            userWalletDetail.setIsAdd(Boolean.TRUE);
            userWalletDetail.setRemark(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? WalletDetailTypeEnum.WANT_BUY_COLLECTION_REFUND.getDescription() : WalletDetailTypeEnum.WANT_BUY_PROPS_REFUND.getDescription());
            userWalletDetailDao.insert(userWalletDetail);

            ExpenditureRecord expenditureRecord = new ExpenditureRecord();
            expenditureRecord.setUserId(wantBuyOrder.getUserId());
            expenditureRecord.setAmount(wantBuyOrder.getUsableBalanceAmount());
            expenditureRecord.setCharge(BigDecimal.ZERO);
            expenditureRecord.setRealAmount(wantBuyOrder.getUsableBalanceAmount());
            expenditureRecord.setType(ExpenditureRecordTypeEnum.REFUND);
            expenditureRecord.setBeforeBalance(userWallet.getBalance());
            expenditureRecord.setAfterBalance(userWallet.getBalance().add(wantBuyOrder.getUsableBalanceAmount()));
            expenditureRecord.setRemark("求购单" + ExpenditureRecordTypeEnum.REFUND.getDescription());
            expenditureRecordDao.insert(expenditureRecord);

        }
        if (wantBuyOrder.getUsablePayBalanceAmount().compareTo(BigDecimal.ZERO) > 0) {
            refundAmount = refundAmount.add(wantBuyOrder.getUsablePayBalanceAmount());

            userWalletDao.addPayBalance(wantBuyOrder.getUserId(), wantBuyOrder.getUsablePayBalanceAmount());

            UserWalletDetail userWalletDetail = new UserWalletDetail();
            userWalletDetail.setUserId(wantBuyOrder.getUserId());
            userWalletDetail.setType(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? WalletDetailTypeEnum.WANT_BUY_COLLECTION_REFUND : WalletDetailTypeEnum.WANT_BUY_PROPS_REFUND);
            userWalletDetail.setBalanceType(UserBalanceTypeEnum.PAY_BALANCE);
            userWalletDetail.setAmount(wantBuyOrder.getUsablePayBalanceAmount());
            userWalletDetail.setCharge(BigDecimal.ZERO);
            userWalletDetail.setRealAmount(wantBuyOrder.getUsablePayBalanceAmount());
            userWalletDetail.setAfterBalance(userWallet.getPayBalance().add(wantBuyOrder.getUsablePayBalanceAmount()));
            userWalletDetail.setBeforeBalance(userWallet.getPayBalance());
            userWalletDetail.setIsAdd(Boolean.TRUE);
            userWalletDetail.setRemark(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? WalletDetailTypeEnum.WANT_BUY_COLLECTION_REFUND.getDescription() : WalletDetailTypeEnum.WANT_BUY_PROPS_REFUND.getDescription());
            userWalletDetailDao.insert(userWalletDetail);

            ExpenditureRecord expenditureRecord = new ExpenditureRecord();
            expenditureRecord.setRemark("求购单" + ExpenditureRecordTypeEnum.REFUND.getDescription());
            expenditureRecord.setUserId(wantBuyOrder.getUserId());
            expenditureRecord.setAmount(wantBuyOrder.getUsablePayBalanceAmount());
            expenditureRecord.setCharge(BigDecimal.ZERO);
            expenditureRecord.setRealAmount(wantBuyOrder.getUsablePayBalanceAmount());
            expenditureRecord.setType(ExpenditureRecordTypeEnum.REFUND);
            expenditureRecord.setBeforeBalance(userWallet.getPayBalance());
            expenditureRecord.setAfterBalance(userWallet.getPayBalance().add(wantBuyOrder.getUsablePayBalanceAmount()));
            expenditureRecordDao.insert(expenditureRecord);

        }
        if (refundAmount.compareTo(BigDecimal.ZERO) > 0) {
            wantBuyOrder.setRefundAmount(wantBuyOrder.getRefundAmount().add(refundAmount));
        }
        //endregion
        wantBuyOrder.setStatus(SecondWantBuyOrderStatusEnum.END);
        wantBuyOrder.setEndTime(DateUtil.date());
        Assert.isTrue(wantBuyOrderDao.updateById(wantBuyOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sell(UserInfo userInfo, WantBuyOrderSellDTO dto, String ip) {
        Date now = DateUtil.date();
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(dto.getId());
        //校验出售请求参数
        validateSellParam(userInfo, wantBuyOrder, dto, now);
        //出售总金额
        BigDecimal payAmount = wantBuyOrder.getWantBuyPrice().multiply(new BigDecimal(dto.getUserRelationIdList().size()));
        //校验求购金额是否足够
        Assert.isTrue(payAmount.compareTo((wantBuyOrder.getUsableBalanceAmount().add(wantBuyOrder.getUsablePayBalanceAmount()))) <= 0, () -> new GlobalRunTimeException("求购单金额不足"));
        //二级订单
        List<SecondOrder> secondOrders = Lists.newArrayList();
        //二级道具
        List<SecondEquityProps> secondEquityPropsList = Lists.newArrayList();
        //二级道具订单
        List<SecondEquityPropsOrder> secondEquityPropsOrders = Lists.newArrayList();
        switch (wantBuyOrder.getOrderType()) {
            case COLLECTION:
                //藏品
                DigitalCollection digitalCollection = digitalCollectionDao.getById(wantBuyOrder.getRelationId());
                Assert.notNull(digitalCollection, () -> new GlobalRunTimeException("藏品不存在"));
                //藏品二级市场
                SecondCollectionConfig secondCollectionConfig = secondCollectionConfigDao.getByCollectionId(digitalCollection.getId());
                //校验出售藏品
                List<UserCollection> userCollectionList = validateSellCollection(userInfo, wantBuyOrder, dto, secondCollectionConfig);
                //region 校验生态藏品，如果求购人和出售人都在共识区，扣减出售人的共识度，增加求购人的共识度
                Boolean isConsensus = validateEcologyCollection(userInfo, wantBuyOrder, dto, digitalCollection);
                //endregion
                //region 如果挂方和买方都是市值账号的话,那么,把手续费设置为0.只收取gas费. 参考二级订单 com.sc.nft.service.impl.NewSecondCollectionCommonServiceImpl#order
                boolean isFilter = validateFilterAccount(digitalCollection.getUserCreatorId(), userInfo.getId(), wantBuyOrder.getUserId());
                //endregion
                //region 校验藏品可到账金额
                validateCollectionAccruedAmount(isFilter, wantBuyOrder, dto, payAmount);
                //endregion
                for (UserCollection userCollection : userCollectionList) {
                    //组装二级藏品
                    SecondCollection secondCollection = buildSecondCollection(userInfo, wantBuyOrder, userCollection, digitalCollection, secondCollectionConfig, isFilter, now);
                    //组装二级订单
                    SecondOrder secondOrder = buildSecondOrder(userInfo, wantBuyOrder, secondCollection, digitalCollection, isConsensus, isFilter, now);
                    secondOrders.add(secondOrder);
                }
                //组装支付订单
                buildPayOrder(wantBuyOrder, secondOrders, payAmount, ip);
                //藏品转移
                secondOrders.forEach(userCollectionService::buy);
                //end 自动结算，不需要手动添加出售人余额，代码：com.sc.nft.task.OrderTask#directPushIncome
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(wantBuyOrder.getRelationId());
                Assert.notNull(equityProps, () -> new GlobalRunTimeException("道具不存在"));
                SecondEquityPropsConfig secondEquityPropsConfig = secondEquityPropsConfigDao.getLastOneByEquityPropsId(equityProps.getId());
                //校验出售道具
                List<UserEquityProps> userEquityPropsList = validateSellProp(userInfo, wantBuyOrder, dto, secondEquityPropsConfig, now);
                //region 校验道具可到账金额
                validatePropAccruedAmount(wantBuyOrder, dto, payAmount);
                //endregion
                //region
                for (UserEquityProps userEquityProps : userEquityPropsList) {
                    //组装二级道具
                    SecondEquityProps secondEquityProps = buildSecondEquityProps(userInfo, wantBuyOrder, userEquityProps, now);
                    //组装二级订单
                    SecondEquityPropsOrder order = buildSecondEquityPropsOrder(wantBuyOrder, secondEquityProps, userEquityProps, equityProps, now);
                    //更新状态为已消耗
                    userEquityPropsService.updateWantBuyOrderToSales(order.getEquityPropsId(), order.getUserEquityPropsId(), order.getId(), order.getSalesUserId(), order.getBuyUserId(), now);
                    secondEquityPropsList.add(secondEquityProps);
                    secondEquityPropsOrders.add(order);
                }
                //endregion
                //组装支付订单
                PayOrder payOrder = buildPayOrder(wantBuyOrder, secondEquityPropsOrders, payAmount, ip, now);
                //道具转移
                userEquityPropsService.buyEquityPropsByUser(equityProps, wantBuyOrder.getUserId(), UserEquityPropsGetTypeEnum.WANT_BUY_ORDER_SELL, userEquityPropsList, "求购单出售" + userEquityPropsList.get(0).getFirstTitle() + userEquityPropsList.size() + "个",
                        payOrder.getId(), wantBuyOrder.getWantBuyPrice());
                //更新最新价格
                equityPropsDao.updateNewDealPrice(equityProps.getId(), wantBuyOrder.getWantBuyPrice());
                //end 自动结算，不需要手动添加出售人余额，代码：com.sc.nft.task.OrderTask#directPushIncome
                break;
        }
        //消耗道具
        consumingProps(wantBuyOrder, dto);
        //更新求购单
        updateOrder(wantBuyOrder, dto, payAmount, now);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                //二级订单
                if (CollectionUtil.isNotEmpty(secondOrders)) {
                    secondOrders.forEach(secondOrder -> {
                        messageQueueHelper.sendMessage(MessageTopic.SECOND_ORDER_TOPIC, secondOrder);
                        MetaMapSettleDTO metaMapSettleDTO = new MetaMapSettleDTO();
                        MetaMapSettleDTO order = metaMapSettleDTO.order(secondOrder.getOrderNo(), secondOrder.getBuyUserId(), MetaMapSettleSourceTypeEnum.SECOND_ORDER, secondOrder.getBuyPrice(), secondOrder.getCharge(), secondOrder.getFirstTitle(), secondOrder.getShareProfitPanelId(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                        messageQueueHelper.sendMessage(MessageTopic.META_MAP_SETTLE, order, MessageTopic.META_MAP_SETTLE, secondOrder.getOrderNo());
                    });
                }
                //二级道具
                if (CollectionUtil.isNotEmpty(secondEquityPropsList)) {
                    secondEquityPropsList.forEach(secondEquityProps -> {
                        messageQueueHelper.sendMessage(MessageTopic.PROPS_FREE_MARKET, secondEquityProps);
                    });
                }
                //二级道具订单
                if (CollectionUtil.isNotEmpty(secondEquityPropsOrders)) {
                    SaasTaskBurialPointDTO saasTaskBurialPointDTO = new SaasTaskBurialPointDTO();
                    messageQueueHelper.sendMessage(MessageTopic.DISK_TAKER, saasTaskBurialPointDTO.wallet(secondEquityPropsOrders.get(0).getBuyUserId(), SaasTaskTypeEnum.NEW_USERS_MAKE_THEIR_FIRST_PURCHASE, Convert.toBigDecimal(1), 0, secondEquityPropsOrders.get(0).getEquityPropsId()), SaasTaskTypeEnum.NEW_USERS_MAKE_THEIR_FIRST_PURCHASE.name());
                }
            }
        });
    }

    @Override
    public WantBuyOrderIncomeVO income(UserInfo userInfo, Long id, Integer size) {
        WantBuyOrderIncomeVO wantBuyOrderIncomeVO = new WantBuyOrderIncomeVO();
        if (size <= 0) {
            return wantBuyOrderIncomeVO;
        }
        WantBuyOrder wantBuyOrder = wantBuyOrderDao.getById(id);
        if (Objects.isNull(wantBuyOrder)) {
            return wantBuyOrderIncomeVO;
        }
        switch (wantBuyOrder.getOrderType()) {
            case COLLECTION:
                DigitalCollection digitalCollection = digitalCollectionDao.getById(wantBuyOrder.getRelationId());
                if (Objects.nonNull(digitalCollection)) {
                    boolean isFilter = validateFilterAccount(digitalCollection.getUserCreatorId(), userInfo.getId(), wantBuyOrder.getUserId());
                    //可到账金额
                    BigDecimal accruedAmount = wantBuyOrder.getWantBuyPrice().multiply(new BigDecimal(size));
                    //手续费
                    BigDecimal totalCharge = BigDecimal.ZERO;
                    //上链费
                    BigDecimal totalGas = BigDecimal.ZERO;
                    if (!isFilter
                            && Objects.nonNull(wantBuyOrder.getCharge())
                            && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0) {
                        totalCharge = wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(size));
                        accruedAmount = accruedAmount.subtract(totalCharge);
                    }
                    if (Objects.nonNull(wantBuyOrder.getGas())
                            && wantBuyOrder.getGas().compareTo(BigDecimal.ZERO) > 0) {
                        totalGas = wantBuyOrder.getGas().multiply(new BigDecimal(size));
                        accruedAmount = accruedAmount.subtract(totalGas);
                    }
                    if (accruedAmount.compareTo(BigDecimal.ZERO) < 0) {
                        accruedAmount = BigDecimal.ZERO;
                    }

                    //region 共识区
                    Boolean isConsensus = Boolean.FALSE;
                    BigDecimal consensus = null;
                    BigDecimal consensusNum = null;
                    if (Objects.equals(digitalCollection.getCollectionType(), DigitalMarketTypeEnum.ECOLOGY)) {
                        ExitConsensusUser sellExitConsensusUser = exitConsensusUserDao.getByUserId(userInfo.getId());
                        isConsensus = ObjectUtil.isNull(sellExitConsensusUser) || !sellExitConsensusUser.getStatus();
                        if (isConsensus) {
                            consensus = new BigDecimal(size);
                            UserCollectionConsensus userCollectionConsensus = userCollectionConsensusDao.getBuyUserIdAndCollectionId(userInfo.getId(), wantBuyOrder.getRelationId());
                            consensusNum = ObjectUtil.isNull(userCollectionConsensus) ? BigDecimal.ZERO : userCollectionConsensus.getConsensusNum();
                        }
                    }
                    //endregion
                    wantBuyOrderIncomeVO.setCharge(totalCharge);
                    wantBuyOrderIncomeVO.setGas(totalGas);
                    wantBuyOrderIncomeVO.setAccruedAmount(accruedAmount);
                    wantBuyOrderIncomeVO.setIsConsensus(isConsensus);
                    wantBuyOrderIncomeVO.setConsensus(consensus);
                    wantBuyOrderIncomeVO.setConsensusNum(consensusNum);
                }
                break;
            case EQUITY_PROPS:
                EquityProps equityProps = equityPropsDao.getById(wantBuyOrder.getRelationId());
                if (Objects.nonNull(equityProps)) {
                    BigDecimal accruedAmount = wantBuyOrder.getWantBuyPrice().multiply(new BigDecimal(size));
                    BigDecimal totalCharge = BigDecimal.ZERO;
                    if (Objects.nonNull(wantBuyOrder.getCharge())
                            && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0
                            && Objects.nonNull(wantBuyOrder.getMinCharge())
                            && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
                        totalCharge = (wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(size)))
                                .min(wantBuyOrder.getMinCharge().multiply(new BigDecimal(size)));
                    } else if (Objects.nonNull(wantBuyOrder.getCharge())
                            && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0) {
                        totalCharge = wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(size));
                    } else if (Objects.nonNull(wantBuyOrder.getMinCharge())
                            && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
                        totalCharge = wantBuyOrder.getMinCharge().multiply(new BigDecimal(size));
                    }
                    if (totalCharge.compareTo(BigDecimal.ZERO) > 0) {
                        accruedAmount = accruedAmount.subtract(totalCharge);
                    }
                    if (accruedAmount.compareTo(BigDecimal.ZERO) < 0) {
                        accruedAmount = BigDecimal.ZERO;
                    }
                    wantBuyOrderIncomeVO.setCharge(totalCharge);
                    wantBuyOrderIncomeVO.setAccruedAmount(accruedAmount);
                }
                break;
        }
        return wantBuyOrderIncomeVO;
    }

    /**
     * 组装支付订单
     *
     * @param wantBuyOrder
     * @param secondEquityPropsOrders
     * @param payAmount
     * @param ip
     * @param now
     */
    private PayOrder buildPayOrder(WantBuyOrder wantBuyOrder, List<SecondEquityPropsOrder> secondEquityPropsOrders, BigDecimal payAmount, String ip, Date now) {
        PayOrder payOrder = new PayOrder();
        payOrder.setPayType(PayMethodEnum.WALLET_PAY);
        payOrder.setReqTime(now);
        payOrder.setUserCardId(null);
        payOrder.setCloseTime(DateUtil.offsetMinute(now, 3));
        payOrder.setOrderNo(secondEquityPropsOrders.get(0).getOrderNo());
        payOrder.setReqIp(ip);
        payOrder.setUserId(wantBuyOrder.getUserId());
        payOrder.setOrderType(PayTypeEnum.SECOND_EQUITY_PROPS);
        payOrder.setPayAmount(payAmount);
        payOrder.setRefundAmount(BigDecimal.ZERO);
        payOrder.setChannel(PayChannelEnum.NATIVE);
        payOrder.setStatus(PayStatusEnum.SUCCESS);
        payOrder.setTradeNo(UUID.fastUUID().toString().replaceAll("-", ""));
        Assert.isTrue(payOrderDao.save(payOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        for (SecondEquityPropsOrder secondOrder : secondEquityPropsOrders) {
            PayOrderItem payOrderItem = new PayOrderItem();
            payOrderItem.setPayOrderId(payOrder.getId());
            payOrderItem.setOrderId(secondOrder.getId());
            payOrderItem.setOrderType(SecondPayOrderItemTypeEnum.EQUITY_PROPS);
            payOrderItem.setAmount(secondOrder.getBuyPrice());
            Assert.isTrue(payOrderItemDao.save(payOrderItem), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        }
        return payOrder;
    }

    /**
     * 组装二级道具订单
     *
     * @param wantBuyOrder
     * @param secondEquityProps
     * @param userEquityProps
     * @param equityProps
     * @param now
     */
    private SecondEquityPropsOrder buildSecondEquityPropsOrder(WantBuyOrder wantBuyOrder, SecondEquityProps secondEquityProps, UserEquityProps userEquityProps, EquityProps equityProps, Date now) {
        SecondEquityPropsOrder order = new SecondEquityPropsOrder();
        order.setSecondEquityPropsId(secondEquityProps.getId());
        order.setEquityPropsId(equityProps.getId());
        order.setUserEquityPropsId(userEquityProps.getId());
        order.setSalesUserId(secondEquityProps.getUserId());
        order.setBuyUserId(wantBuyOrder.getUserId());
        order.setBuyPrice(wantBuyOrder.getWantBuyPrice());
        order.setCharge(secondEquityProps.getCharge());
        order.setStatus(OrderStatusEnum.PAYMENT);
        order.setOrderNo(OrderNoUtil.getOrderNo(String.valueOf(PayTypeEnum.SECOND_EQUITY_PROPS.getCode())));
        order.setDealTime(now);
        order.setCloseTime(DateUtil.offsetMinute(now, 3));
        order.setRealAmount(order.getBuyPrice().subtract(order.getCharge()));
        order.setFirstTitle(equityProps.getFirstTitle());
        order.setBuySumCount(1);
        order.setIsSettlePay(false);
        order.setReceiveType(PayMethodEnum.WALLET_PAY);
        order.setIsSecondWantBuyOrder(Boolean.TRUE);
        order.setSecondWantBuyOrderId(wantBuyOrder.getId());
        Assert.isTrue(secondEquityPropsOrderDao.save(order), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        return order;
    }

    /**
     * 组装二级道具
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param userEquityProps
     * @param now
     * @return
     */
    private SecondEquityProps buildSecondEquityProps(UserInfo userInfo, WantBuyOrder wantBuyOrder, UserEquityProps userEquityProps, Date now) {
        SecondEquityProps secondEquityProps = new SecondEquityProps();
        secondEquityProps.setEquityPropsId(userEquityProps.getEquityPropsId());
        secondEquityProps.setUserEquityPropsId(userEquityProps.getId());
        secondEquityProps.setUserId(userInfo.getId());
        secondEquityProps.setStatus(ConSignStatus.SUCCESS);
        secondEquityProps.setOnShelfTime(now);
        secondEquityProps.setOffShelfTime(DateUtil.offset(now, DateField.MINUTE, 3));
        BigDecimal charge = BigDecimal.ZERO;
        if (Objects.nonNull(wantBuyOrder.getCharge())
                && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0
                && Objects.nonNull(wantBuyOrder.getMinCharge())
                && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
            charge = (wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge())).min(wantBuyOrder.getMinCharge());
        } else if (Objects.nonNull(wantBuyOrder.getCharge())
                && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0) {
            charge = wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge());
        } else if (Objects.nonNull(wantBuyOrder.getMinCharge())
                && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
            charge = wantBuyOrder.getMinCharge();
        }
        secondEquityProps.setCharge(charge);
        secondEquityProps.setSalesPrice(wantBuyOrder.getWantBuyPrice());
        Assert.isTrue(secondEquityPropsDao.save(secondEquityProps), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        return secondEquityProps;
    }

    /**
     * 组装支付订单
     *
     * @param wantBuyOrder
     * @param secondOrders
     * @param payAmount
     * @param ip
     */
    private void buildPayOrder(WantBuyOrder wantBuyOrder, List<SecondOrder> secondOrders, BigDecimal payAmount, String ip) {
        PayOrder payOrder = new PayOrder();
        payOrder.setUserId(wantBuyOrder.getUserId());
        payOrder.setPayType(PayMethodEnum.WALLET_PAY);
        payOrder.setReqTime(DateUtil.date());
        payOrder.setCloseTime(DateUtil.date());
        payOrder.setOrderNo(secondOrders.get(0).getOrderNo());
        payOrder.setReqIp(ip);
        payOrder.setOrderType(PayTypeEnum.SECOND);
        payOrder.setPayAmount(payAmount);
        payOrder.setChannel(PayChannelEnum.NATIVE);
        payOrder.setStatus(PayStatusEnum.SUCCESS);
        payOrder.setPayTime(DateUtil.date());
        payOrder.setPayBalanceAmount(BigDecimal.ZERO);
        Assert.isTrue(payOrderDao.save(payOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        for (SecondOrder secondOrder : secondOrders) {
            PayOrderItem payOrderItem = new PayOrderItem();
            payOrderItem.setPayOrderId(payOrder.getId());
            payOrderItem.setOrderId(secondOrder.getId());
            payOrderItem.setAmount(secondOrder.getBuyPrice());
            Assert.isTrue(payOrderItemDao.save(payOrderItem), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        }
    }

    /**
     * 组装二级订单
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param secondCollection
     * @param digitalCollection
     * @param isConsensus
     * @param isFilter
     * @param now
     */
    private SecondOrder buildSecondOrder(UserInfo userInfo, WantBuyOrder wantBuyOrder, SecondCollection secondCollection, DigitalCollection digitalCollection, Boolean isConsensus, Boolean isFilter, Date now) {
        //region 锁单 com.sc.nft.service.impl.NewSecondCollectionCommonServiceImpl#lockOrder
        SecondOrder secondOrder = new SecondOrder();
        secondOrder.setFirstTitle(digitalCollection.getFirstTitle());
        secondOrder.setCollectionSumCount(digitalCollection.getSumCount());
        secondOrder.setNumbering(secondCollection.getNumbering());
        secondOrder.setSecondCollectionId(secondCollection.getId());
        secondOrder.setCollectionId(secondCollection.getCollectionId());
        secondOrder.setUserCollectionId(secondCollection.getUserCollectionId());
        secondOrder.setSalesUserId(userInfo.getId());
        secondOrder.setIsConsensus(isConsensus);
        secondOrder.setBuyUserId(wantBuyOrder.getUserId());
        secondOrder.setShareProfitPanelId(secondCollection.getShareProfitPanelId());
        secondOrder.setBuyPrice(secondCollection.getSalesPrice());
        // 如果挂方和买方都是市值账号的话,那么,把手续费设置为0.只收取gas费. 参考二级订单 com.sc.nft.service.impl.NewSecondCollectionCommonServiceImpl#order
        secondOrder.setCharge(isFilter ? BigDecimal.ZERO : secondCollection.getCharge());
        secondOrder.setGas(wantBuyOrder.getGas());
        secondOrder.setCloseTime(DateUtil.offsetMinute(new Date(), 3));
        secondOrder.setStatus(OrderStatusEnum.PAYMENT); //已付款
        secondOrder.setBuyPayBalancePrice(BigDecimal.ZERO);
        secondOrder.setPayTime(now);
        secondOrder.setOrderNo(OrderNoUtil.getOrderNo());
        secondOrder.setDealTime(DateTime.now());
        // 二级市场的全部都给钱.
        secondOrder.setPreventHoard(BigDecimal.ZERO);
        secondOrder.setMarketType(MarketTypeEnum.WANT_BUY_ORDER);
        secondOrder.setSettleFlag(false);
        secondOrder.setPerformanceSettleFlag(secondOrder.getSettleFlag());
        secondOrder.setRealAmount(secondOrder.getBuyPrice().subtract(secondOrder.getCharge()).subtract(secondOrder.getGas()));
        secondOrder.setIsSecondWantBuyOrder(Boolean.TRUE);
        secondOrder.setSecondWantBuyOrderId(wantBuyOrder.getId());
        Assert.isTrue(secondOrderDao.save(secondOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        return secondOrder;
    }

    /**
     * 组装二级藏品
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param userCollection
     * @param digitalCollection
     * @param secondCollectionConfig
     * @param isFilter
     * @param now
     */
    private SecondCollection buildSecondCollection(UserInfo userInfo, WantBuyOrder wantBuyOrder, UserCollection userCollection, DigitalCollection digitalCollection, SecondCollectionConfig secondCollectionConfig, Boolean isFilter, Date now) {
        //参考自由市场上架 com.sc.nft.service.impl.NewSecondCollectionCommonServiceImpl#freeMarketHitShelf
        SecondCollection secondCollection = new SecondCollection();
        secondCollection.setCollectionId(userCollection.getCollectionId());
        secondCollection.setUserCollectionId(userCollection.getId());
        secondCollection.setUserId(userInfo.getId());
        secondCollection.setStatus(ConSignStatus.SUCCESS);
        secondCollection.setOnShelfTime(now);
        secondCollection.setSalesTime(now);
        secondCollection.setShareProfitPanelId(secondCollectionConfig.getShareProfitPanelId());
        secondCollection.setOffShelfTime(DateUtil.endOfDay(secondCollection.getOnShelfTime()));
        secondCollection.setCollectionSumCount(digitalCollection.getSumCount());
        secondCollection.setNumbering(userCollection.getNumbering());
        secondCollection.setSalesPrice(wantBuyOrder.getWantBuyPrice());
        secondCollection.setCharge(isFilter ? BigDecimal.ZERO : wantBuyOrder.getCharge().multiply(wantBuyOrder.getWantBuyPrice()));
        if (BooleanUtil.isTrue(userCollection.getAntiHoardFlag())) {
            secondCollection.setAntiHoardPrice(userCollection.getAntiHoardPrice());
        } else {
            secondCollection.setAntiHoardPrice(BigDecimal.ZERO);
        }
        secondCollection.setMinPrice(secondCollectionConfig.getMinWantBuyPrice());
        secondCollection.setMaxPrice(secondCollectionConfig.getMaxWantBuyPrice());
        secondCollection.setMarketType(secondCollectionConfig.getMarketType());
        secondCollection.setCreateId(userInfo.getId());
        Assert.isTrue(secondCollectionDao.save(secondCollection), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
        return secondCollection;
    }

    /**
     * 更新求购单
     *
     * @param wantBuyOrder
     * @param dto
     * @param payAmount
     * @param now
     */
    private void updateOrder(WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, BigDecimal payAmount, Date now) {
        BigDecimal usableAmount = payAmount;
        //region 优先扣减原石
        if (wantBuyOrder.getUsablePayBalanceAmount().compareTo(BigDecimal.ZERO) > 0) {
            if (wantBuyOrder.getUsablePayBalanceAmount().compareTo(usableAmount) >= 0) {
                usableAmount = BigDecimal.ZERO;
                wantBuyOrder.setUsablePayBalanceAmount(wantBuyOrder.getUsablePayBalanceAmount().subtract(usableAmount));
            } else {
                usableAmount = usableAmount.subtract(wantBuyOrder.getUsablePayBalanceAmount());
                wantBuyOrder.setUsablePayBalanceAmount(BigDecimal.ZERO);
            }
        }
        //endregion
        //region 再扣减余额
        if (usableAmount.compareTo(BigDecimal.ZERO) > 0) {
            wantBuyOrder.setUsableBalanceAmount(wantBuyOrder.getUsableBalanceAmount().subtract(usableAmount));
        }
        //endregion
        //region 扣减可求购数量
        wantBuyOrder.setUsableWantBuyQuantity(wantBuyOrder.getUsableWantBuyQuantity() - dto.getUserRelationIdList().size());
        //endregion
        //region 如果可求购数量为0，关闭求购单
        if (wantBuyOrder.getUsableWantBuyQuantity() == 0) {
            wantBuyOrder.setStatus(SecondWantBuyOrderStatusEnum.END);
            wantBuyOrder.setEndTime(now);
        }
        //endregion
        Assert.isTrue(wantBuyOrderDao.updateById(wantBuyOrder), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
    }

    /**
     * 消耗道具
     *
     * @param wantBuyOrder
     * @param dto
     */
    private void consumingProps(WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto) {
        if (wantBuyOrder.getEnableWantBuyConsumeProp()) {
            int size = wantBuyOrder.getWantBuyConsumePropQuantity() * dto.getUserRelationIdList().size();
            List<WantBuyOrderConsumeProp> wantBuyOrderConsumePropList = wantBuyOrderConsumePropDao.queryByOrderIdAndStatusLimit(wantBuyOrder.getId(), SecondWantBuyOrderConsumePropStatusEnum.OCCUPY, size);
            Assert.isTrue(CollectionUtil.isNotEmpty(wantBuyOrderConsumePropList) && wantBuyOrderConsumePropList.size() == size, () -> new GlobalRunTimeException("待消耗道具不足"));
            wantBuyOrderConsumePropList.forEach(wantBuyOrderConsumeProp -> {
                UserEquityProps userEquityProps = userEquityPropsDao.getById(wantBuyOrderConsumeProp.getUserEquityPropsId());
                Assert.notNull(userEquityProps, () -> new GlobalRunTimeException("道具不存在"));
                wantBuyOrderConsumeProp.setStatus(SecondWantBuyOrderConsumePropStatusEnum.CONSUME);
                Assert.isTrue(wantBuyOrderConsumePropDao.updateById(wantBuyOrderConsumeProp), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
                Assert.isTrue(userEquityPropsDao.updateFromStatusToStatus(userEquityProps.getId(), UserEquityPropsHoldingStatusEnum.WANT_BUY_LOCK, UserEquityPropsHoldingStatusEnum.WANT_BUY_CONSUME, UserEquityConsignStatusEnum.NOT_CONSIGN), () -> new GlobalRunTimeException(ErrorCode.UPDATE_ERROR));
            });
        }
    }

    /**
     * 校验藏品可到账金额
     *
     * @param isFilter
     * @param wantBuyOrder
     * @param dto
     * @param payAmount
     */
    private void validateCollectionAccruedAmount(Boolean isFilter, WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, BigDecimal payAmount) {
        BigDecimal accruedAmount = payAmount;
        if (!isFilter
                && Objects.nonNull(wantBuyOrder.getCharge())
                && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal totalCharge = wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(dto.getUserRelationIdList().size()));
            accruedAmount = accruedAmount.subtract(totalCharge);
        }
        if (Objects.nonNull(wantBuyOrder.getGas())
                && wantBuyOrder.getGas().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal totalGas = wantBuyOrder.getGas().multiply(new BigDecimal(dto.getUserRelationIdList().size()));
            accruedAmount = accruedAmount.subtract(totalGas);
        }
        Assert.isTrue(accruedAmount.compareTo(BigDecimal.ZERO) > 0, () -> new GlobalRunTimeException("可支付金额不足"));
    }

    /**
     * 计算道具可到账金额
     *
     * @param wantBuyOrder
     * @param dto
     * @param payAmount
     */
    private void validatePropAccruedAmount(WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, BigDecimal payAmount) {
        BigDecimal accruedAmount = payAmount;
        BigDecimal totalCharge = BigDecimal.ZERO;
        if (Objects.nonNull(wantBuyOrder.getCharge())
                && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0
                && Objects.nonNull(wantBuyOrder.getMinCharge())
                && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
            totalCharge = (wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(dto.getUserRelationIdList().size())))
                    .min(wantBuyOrder.getMinCharge().multiply(new BigDecimal(dto.getUserRelationIdList().size())));
        } else if (Objects.nonNull(wantBuyOrder.getCharge())
                && wantBuyOrder.getCharge().compareTo(BigDecimal.ZERO) > 0) {
            totalCharge = wantBuyOrder.getWantBuyPrice().multiply(wantBuyOrder.getCharge()).multiply(new BigDecimal(dto.getUserRelationIdList().size()));
        } else if (Objects.nonNull(wantBuyOrder.getMinCharge())
                && wantBuyOrder.getMinCharge().compareTo(BigDecimal.ZERO) > 0) {
            totalCharge = wantBuyOrder.getMinCharge().multiply(new BigDecimal(dto.getUserRelationIdList().size()));
        }
        if (totalCharge.compareTo(BigDecimal.ZERO) > 0) {
            accruedAmount = accruedAmount.subtract(totalCharge);
        }
        Assert.isTrue(accruedAmount.compareTo(BigDecimal.ZERO) > 0, () -> new GlobalRunTimeException("可支付金额不足"));
    }

    /**
     * 校验出售请求参数
     *
     * @param userInfo
     * @param dto
     * @param now
     */
    private void validateSellParam(UserInfo userInfo, WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, Date now) {
        Assert.isTrue(StrUtil.equals(dto.getPaymentPassword(), UserPool.getUser().getTransactionPassword()), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_PASSWORD_WAS_ENTERED_INCORRECTLY));
        Assert.notNull(wantBuyOrder, () -> new GlobalRunTimeException("求购单不存在"));
        Assert.isTrue(Objects.equals(wantBuyOrder.getStatus(), SecondWantBuyOrderStatusEnum.RELEASE), () -> new GlobalRunTimeException("求购单已结束"));
        Assert.isTrue(wantBuyOrder.getEndTime().after(now), () -> new GlobalRunTimeException("求购单已结束"));
        Assert.isTrue(wantBuyOrder.getUsableWantBuyQuantity() > 0, () -> new GlobalRunTimeException("求购单已结束"));
        Assert.isFalse(Objects.equals(wantBuyOrder.getUserId(), userInfo.getId()), () -> new GlobalRunTimeException("禁止出售自己创建的求购单"));
        Assert.isTrue(CollectionUtil.isNotEmpty(dto.getUserRelationIdList()), () -> new GlobalRunTimeException(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? "请先选择藏品" : "请先选择道具"));
        Assert.isTrue(dto.getUserRelationIdList().size() == (int) dto.getUserRelationIdList().stream().distinct().count(), () -> new GlobalRunTimeException(Objects.equals(wantBuyOrder.getOrderType(), SecondWantBuyOrderTypeEnum.COLLECTION) ? "所选藏品重复" : "所选道具重复"));
        Assert.isTrue(wantBuyOrder.getUsableWantBuyQuantity() >= dto.getUserRelationIdList().size(), () -> new GlobalRunTimeException("待求购数量不足"));
    }

    /**
     * 校验出售道具
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param dto
     * @param secondEquityPropsConfig
     */
    private List<UserEquityProps> validateSellProp(UserInfo userInfo, WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, SecondEquityPropsConfig secondEquityPropsConfig, Date now) {
        Assert.notNull(secondEquityPropsConfig, () -> new GlobalRunTimeException("道具未开启寄售"));
        Assert.isTrue(secondEquityPropsConfig.getStatus(), () -> new GlobalRunTimeException("道具未开启寄售"));
        List<UserEquityProps> userEquityPropsList = userEquityPropsDao.listByIds(dto.getUserRelationIdList());
        Assert.isTrue(CollectionUtil.isNotEmpty(userEquityPropsList) && userEquityPropsList.size() == dto.getUserRelationIdList().size(), () -> new GlobalRunTimeException("所选道具不存在"));
        userEquityPropsList.forEach(userEquityProps -> {
            Assert.isTrue(Objects.equals(userEquityProps.getUserId(), userInfo.getId()), () -> new GlobalRunTimeException("道具不属于本人"));
            Assert.isTrue(Objects.equals(userEquityProps.getHoldingStatus(), UserEquityPropsHoldingStatusEnum.NORMAL), () -> new GlobalRunTimeException("本人不持有该道具"));
            Assert.isTrue(Objects.equals(userEquityProps.getOnConsign(), UserEquityConsignStatusEnum.NOT_CONSIGN), () -> new GlobalRunTimeException("道具寄售中"));
            Assert.isTrue(userEquityProps.getExpiredTime().after(now), () -> new GlobalRunTimeException("道具已过期"));
            Assert.isTrue(Objects.equals(wantBuyOrder.getRelationId(), userEquityProps.getEquityPropsId()), () -> new GlobalRunTimeException("所选道具与求购单不匹配"));
        });
        return userEquityPropsList;
    }

    /**
     * 校验出售藏品
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param dto
     */
    private List<UserCollection> validateSellCollection(UserInfo userInfo, WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, SecondCollectionConfig secondCollectionConfig) {
        Assert.notNull(secondCollectionConfig, () -> new GlobalRunTimeException("藏品未开启寄售"));
        Assert.isTrue(secondCollectionConfig.getStatus(), () -> new GlobalRunTimeException("藏品未开启寄售"));
        List<UserCollection> userCollectionList = userCollectionDao.listByIds(dto.getUserRelationIdList());
        Assert.isTrue(CollectionUtil.isNotEmpty(userCollectionList) && userCollectionList.size() == dto.getUserRelationIdList().size(), () -> new GlobalRunTimeException("所选藏品不存在"));
        userCollectionList.forEach(userCollection -> {
            Assert.isTrue(Objects.equals(userCollection.getUserId(), userInfo.getId()), () -> new GlobalRunTimeException("藏品不属于本人"));
            Assert.isTrue(Objects.equals(userCollection.getIsHold(), Boolean.TRUE), () -> new GlobalRunTimeException("本人不持有该藏品"));
            Assert.isTrue(Objects.equals(userCollection.getIsOpen(), Boolean.TRUE), () -> new GlobalRunTimeException("藏品未开启"));
            Assert.isTrue(Objects.equals(userCollection.getOnConsign(), CollectionOnConsignEnum.NORMAL), () -> new GlobalRunTimeException("藏品寄售中"));
            Assert.isTrue(Objects.equals(userCollection.getFreezingStatus(), UserCollectionFreezingStatusEnum.NORMAL), () -> new GlobalRunTimeException("藏品激活中/质押中"));
            Assert.isTrue(Objects.equals(wantBuyOrder.getRelationId(), userCollection.getCollectionId()), () -> new GlobalRunTimeException("所选藏品与求购单不匹配"));
        });
        return userCollectionList;
    }

    /**
     * 校验生态藏品
     *
     * @param userInfo
     * @param wantBuyOrder
     * @param dto
     * @param digitalCollection
     * @return
     */
    private Boolean validateEcologyCollection(UserInfo userInfo, WantBuyOrder wantBuyOrder, WantBuyOrderSellDTO dto, DigitalCollection digitalCollection) {
        Boolean isConsensus = Boolean.FALSE;
        if (Objects.equals(digitalCollection.getCollectionType(), DigitalMarketTypeEnum.ECOLOGY)) {
            ExitConsensusUser sellExitConsensusUser = exitConsensusUserDao.getByUserId(userInfo.getId());
            isConsensus = ObjectUtil.isNull(sellExitConsensusUser) || !sellExitConsensusUser.getStatus();
            ExitConsensusUser wantBuyExitConsensusUser = exitConsensusUserDao.getByUserId(wantBuyOrder.getUserId());
            Boolean wantBuyIsConsensus = ObjectUtil.isNull(wantBuyExitConsensusUser) || !wantBuyExitConsensusUser.getStatus();
            Assert.isTrue(Objects.equals(isConsensus, wantBuyIsConsensus), isConsensus ? () -> new GlobalRunTimeException("求购人不在共识区") : () -> new GlobalRunTimeException("求购人在共识区"));
            if (isConsensus) {
                //针对生态藏品，出售人在共识区，校验共识度
                UserCollectionConsensus userCollectionConsensus = userCollectionConsensusDao.getBuyUserIdAndCollectionId(userInfo.getId(), wantBuyOrder.getRelationId());
                BigDecimal consensusNum = ObjectUtil.isNull(userCollectionConsensus) ? BigDecimal.ZERO : userCollectionConsensus.getConsensusNum();
                Assert.isTrue(consensusNum.compareTo(new BigDecimal(dto.getUserRelationIdList().size())) >= 0, () -> new GlobalRunTimeException("共识度不足"));
                //扣除出售人共识度
                userConsensusService.adjustConsensusService(userInfo.getId(), digitalCollection.getId(), Convert.toBigDecimal(-dto.getUserRelationIdList().size()), UserConsensusTypeEnums.WANT_BUY_ORDER_SELL, digitalCollection.getFirstTitle(), null);
                //增加求购人共识度
                userConsensusService.adjustConsensusService(wantBuyOrder.getUserId(), digitalCollection.getId(), Convert.toBigDecimal(dto.getUserRelationIdList().size()), UserConsensusTypeEnums.WANT_BUY_ORDER_BUY, digitalCollection.getFirstTitle(), null);
            }
        }
        return isConsensus;
    }

    /**
     * 校验是否市值账号
     * 如果挂方和买方都是市值账号的话,那么,把手续费设置为0.只收取gas费. 参考二级订单 com.sc.nft.service.impl.NewSecondCollectionCommonServiceImpl#order
     *
     * @param daoId
     * @param sellUserId
     * @param buyUserId
     * @return
     */
    private Boolean validateFilterAccount(Long daoId, Long sellUserId, Long buyUserId) {
        return filterIncomeListDao.checkIsFilterAccount(daoId, sellUserId) && filterIncomeListDao.checkIsFilterAccount(daoId, buyUserId);
    }

    /**
     * 计算剩余时间，单位分钟
     *
     * @param endTime
     * @param now
     * @return
     */
    private String timeDifference(Date endTime, Date now) {
        // 计算 endTime 与 currentTime 的分钟差
        long diffInMinutes = (endTime.getTime() - now.getTime()) / (1000 * 60);  // 得到相差的分钟数
        if (diffInMinutes < 0) {
            diffInMinutes = 0;
        }
        // 判断是否大于等于 60 分钟
        if (diffInMinutes >= 60) {
            long hours = diffInMinutes / 60;  // 计算小时数
            long minutes = diffInMinutes % 60;  // 计算剩余的分钟数
            return String.format("%02d小时%02d分钟", hours, minutes);
        } else {
            // 如果不到一小时
            return String.format("0小时%02d分钟", diffInMinutes);
        }
    }

}